// Preset / routine system
// Author:
//    gumi
// Description:
//    allows to execute multiple commands in a single step
//    to ease testing and debugging
//
// usage: @pre [options] <instruction>[, <instruction>...]
// usage: DoRoutine "[options] <instruction>[, <instruction>...]";
//
// example: @pre -s a22 v14
//          resets all stats, gives 22 agi, gives 14 vit
//
// ^ actual documentation may come one day, when I feel like it
//   *hides*

function	script	DoRoutine	{

    function parsev {
        // parsev(base, patterns{, min{, max}}) => value
        .@value = getarg(0, 0);
        .@raw$ = getarg(1, "");
        .@patterns = explode(.@patterns$, .@raw$, "|");

        for (.@pattern = 0; .@pattern < .@patterns; ++.@pattern)
        {
            .@pattern$ = .@patterns$[.@pattern];
            .@len = getstrlen(.@pattern$);

            if (charat(.@pattern$, 0) == "=" && .@len >= 2)
            {
                .@value = atoi(delchar(.@pattern$, 0));
                break;
            }

            else if (charat(.@pattern$, 0) == "+" && .@len >= 2)
            {
                if (charat(.@pattern$, 1) == "+" && getargcount() >= 4)
                    .@value = getarg(3);
                else
                    .@value += atoi(delchar(.@pattern$, 0));
            }

            else if (charat(.@pattern$, 0) == "-" && .@len >= 2)
            {
                if (charat(.@pattern$, 1) == "-" && getargcount() >= 3)
                    .@value = getarg(2);
                else
                    .@value -= atoi(delchar(.@pattern$, 0));
            }

            else if (charat(.@pattern$, 0) == ">" && .@len >= 2)
            {
                if (charat(.@pattern$, 1) == "=" && .@len >= 3)
                    .@value = max(atoi(substr(.@pattern$, 2, .@len - 1)), .@value);
                else
                    .@value = max(atoi(delchar(.@pattern$, 0)) + 1, .@value);
            }

            else if (charat(.@pattern$, 0) == "<" && .@len >= 2)
            {
                if (charat(.@pattern$, 1) == "=" && .@len >= 3)
                    .@value = min(atoi(substr(.@pattern$, 2, .@len - 1)), .@value);
                else
                    .@value = min(atoi(delchar(.@pattern$, 0)) - 1, .@value);
            }

            else if (.@len >= 1)
            {
                .@value = atoi(.@pattern$);
                break;
            }
        }

        if (getarg(2, 0) && getargcount() >= 3)
            .@value = max(getarg(2), .@value);

        if (getarg(3, 0) && getargcount() >= 4)
            .@value = min(getarg(3), .@value);

        return .@value;
    }

    .@routine$ = strip(getarg(0,""));
    .@m = explode(.@routine$[0], .@routine$, " "); // prep the base array

    if (charat(.@routine$[0], 0) == "-")
    {
        if (compare(.@routine$[0], "t"))
        {
            clearitem;
        }

        if (compare(.@routine$[0], "e"))
        {
            nude;
        }

        if (compare(.@routine$[0], "k"))
        {
            resetskill;
        }

        if (compare(.@routine$[0], "s"))
        {
            resetstatus;
        }

        if (compare(.@routine$[0], "x"))
        {
            resetlvl 2;
        }

        if (compare(.@routine$[0], "q"))
        {
            //doevent "::OnGlobalQuestReset"; // executes in all quest npcs // FIXME: maybe have a `resetquest` buildin?
            // FIXME: ^ need a buildin that can run *right now* instead of on script end
        }

        if (compare(.@routine$[0], "n"))
        {
            closeclientdialog;
        }

        .@i = 1;
    }

    for (; .@i < .@m; ++.@i)
    {
        .@type = ord(charat(strip(.@routine$[.@i]), 0));

        if (.@type > 0)
        {
            .@raw$ = delchar(.@routine$[.@i], 0);
            .@args = explode(.@args$, .@raw$, ",");
            .@a = atoi(.@args$[0]);
            .@b = atoi(.@args$[1]);
            .@c = atoi(.@args$[2]);
            .@d = atoi(.@args$[3]);
            .@e = atoi(.@args$[4]);

            switch (.@type)
            {
            case 97: /* a => Agi */
                .@base = readparam(bAgi);
                statusup2 bAgi, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            /* (b) */
            case 99: /* c => job */
                jobchange max(0, min(6, .@a));
                break;
            case 100: /* d => Dex */
                .@base = readparam(bDex);
                statusup2 bDex, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            case 101: /* e => equip */
                equip max(1, min(32767, .@a));
                break;
            /* (f) */
            /* (g) => gender (reserved) */
            /* (h) */
            case 105: /* i => Int */
                .@base = readparam(bInt);
                statusup2 bInt, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            /* (j) */
            case 107: /* k => skill */
                .@k = max(1, min(32767, .@a));
                skill .@k, parsev(getskilllv(.@k), .@args$[1], 0, 10);
                break;
            case 108: /* l => luk */
                .@base = readparam(bLuk);
                statusup2 bLuk, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            /* (m) => mercenary (reserved) */
            /* (n) => talk to npc (reserved) */
            /* (o) */
            case 112: /* p => pet */
                makepet max(1002, min(32767, .@a));
                break;
            case 113: /* q => quest */
                .@q = max(0, min(32767, .@a));
                setq .@q, parsev(getq(.@q), .@args$[1]),
                          parsev(getq2(.@q), .@args$[2]),
                          parsev(getq3(.@q), .@args$[3]),
                          parsev(getqtime(.@q), .@args$[4]);
                break;
            /* (r) */
            case 115: /* s => Str */
                .@base = readparam(bStr);
                statusup2 bStr, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            case 116: /* t => item */
                .@t = max(1, min(32767, .@a));
                .@c = countitem(.@t);
                .@v = parsev(.@c, .@args$[1], 0, 32767);
                if (.@c > .@v)
                    delitem .@t, .@c - .@v;
                else if (.@c < .@v)
                    getitem .@t, .@v - .@c;
                break;
            /* (u) */
            case 118: /* v => vit */
                .@base = readparam(bVit);
                statusup2 bVit, parsev(.@base, .@args$[0], 1, 99) - .@base;
                break;
            case 119: /* w => warp */
                warp .@args$[0], .@b, .@c;
                break;
            case 120: /* x => base level */
                BaseLevel = parsev(BaseLevel, .@args$[0], 1, 99);
                BaseExp = parsev(BaseExp, .@args$[1], 0);
                break;
            case 121: /* y => job level */
                JobLevel = parsev(JobLevel, .@args$[0], 1, 255);
                JobExp = parsev(JobExp, .@args$[1], 0);
                break;
            case 122: /* z => zeny */
                Zeny = parsev(Zeny, .@args$[0], 0, 0x7FFFFFFE);
                #MerchantBank = parsev(#MerchantBank, .@args$[1], 0, 0x7FFFFFFF);
                break;
            }
        }
    }

    return;
}

-	script	@pre	32767,{
    end;

OnCall:
    if (!debug && getgroupid() < 99)
    {
        end;
    }
    if (.@atcmd_parameters$[0] != "")
    {
        .@atcmd_parameters$[0] = implode(.@atcmd_parameters$[0], " ");
    }
    DoRoutine strip(.@atcmd_parameters$[0]);
    end;

OnInit:
    bindatcmd "pre", "@pre::OnCall", 0, 99, 0;
}
